﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using Ext.Net;
using Ext.Net.Utilities;


namespace KamLib
{

    public class EntityDescAttribute : Attribute
    {
        public string Name = "record";
        public string Names = "record";
        public string DetailName = "record";
        public string DetailNames = "record";
        public EntityDescAttribute() { }
        public EntityDescAttribute(string name) { Name = name; }
        public EntityDescAttribute(string name, string detailName)
        {
            Name = name;
            DetailName = detailName;
        }

        public virtual void InitCheck()
        {
            if (Names.IsEmpty())
                Names = Name + "s";
            if (DetailName.IsEmpty())
                DetailName = Name;
            if (DetailNames.IsEmpty())
                DetailNames = DetailName + "s";
        }
    }

    [Flags]
    public enum UilResyncMode
    {
        Close = 1,
        Delete = 2,
    }

    public class UilResyncKey
    {
        public UiKey Key;
        public UilResyncMode Mode;
    }

    public class UilResyncKeys : List<UilResyncKey>
    {
        public UilResyncKeys Resync(UiKey key, UilResyncMode mode)
        {
            if (key != null)
                Add(new UilResyncKey { Key = key, Mode = mode });
            return this;
        }
        public UilResyncKeys Update(UiKey key)
        {
            if (key != null)
                Add(new UilResyncKey { Key = key });
            return this;
        }
        public UilResyncKeys Delete(UiKey key)
        {
            if (key != null)
                Add(new UilResyncKey { Key = key, Mode = UilResyncMode.Delete });
            return this;
        }
    }


    public abstract class UilNode : IDisposable
    {
        public UilController Controller { get; protected set; }
        public string LID { get { return Controller.LID; } }
        public string LIID;
        public UiKey Key;
        public string[] KeyFields;
        public string[] DependentStoreIDs;
        public List<UiKey> DependentKeys;

        public bool IsLocal;

        public XControlResponse Response;
        public EntityDescAttribute EntityDesc;

        public readonly UilNodeDetails Details = new UilNodeDetails();

        public virtual UilNode Initialize(UilController controller, string liid, UiKey key)
        {
            Controller = controller;
            LIID = liid;
            Key = key;

            EntityDesc = null;
            var t = GetType();
            while (t != null && EntityDesc == null)
            {
                EntityDesc = (EntityDescAttribute)t.GetCustomAttributes(typeof(EntityDescAttribute), true).FirstOrDefault();
                t = t.ReflectedType;
            }
            if (EntityDesc == null)
                throw new Exception("Can't find EntityDescAttribute for class " + GetType().FullName);

            Init();
            InitCheck();
            EntityDesc.InitCheck();

            AfterInit();
            return this;
        }

        public virtual void Init() { }
        public virtual void InitCheck() { }
        public virtual void AfterInit() { }
        public virtual void Activate() { }
        public virtual void Dispose() { }
        public abstract object GetResyncData(UilResyncKey key);


        public virtual string GetControlIDPrefix() { return Controller.LID + "_"; }

        private string _ControlID;
        public string ControlID
        {
            get
            {
                return _ControlID
                    ?? (_ControlID = Key != null ? Key.CID : null)
                    ?? (_ControlID = GetControlIDPrefix() + Controller.GetNextControlID()).With(cid => { if (Key != null) Key.CID = cid; });
            }
            set { _ControlID = value; }
        }

        /// <summary>
        /// Содержит созданный control
        /// </summary>
        public EquipInterfaceContainer EquipContainer;


        #region AddDetail

        public UilNodeDetail AddDetail<T>(T logic, string[] masterFields, string[] detailFields, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            if (logic.Key == null && Key != null && Key.DetailKeys != null)
                logic.Key = Key.DetailKeys.Where(a => a.LID == logic.Controller.LID && a.LIID == logic.LIID).FirstOrDefault();
            var d = Details.Add(logic, masterFields, detailFields, init);
            if (logic.Key == null && Key != null)
            {
                logic.Key = new UiKey(logic);
                //d.LoadMasterID(Key);
            }
            return d;
        }
        public UilNodeDetail AddDetail<T>(T logic, string masterField, string detailField, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            return AddDetail(logic, new[] { masterField }, new[] { detailField }, init);
        }
        public UilNodeDetail AddDetail<T>(T logic, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            return AddDetail(logic, (string[])null, null, init);
        }

        public UilNodeDetail AddDetail(string liid, string[] masterFields, string[] detailFields, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(null, liid);
            return AddDetail(logic, masterFields, detailFields, init);
        }
        public UilNodeDetail AddDetail(string liid, string masterField, string detailField, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(null, liid);
            return AddDetail(logic, masterField, detailField, init);
        }
        public UilNodeDetail AddDetail(string liid, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(null, liid);
            return AddDetail(logic, init);
        }

        public UilNodeDetail AddDetail(string lid, string liid, string[] masterFields, string[] detailFields, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(lid, liid);
            return AddDetail(logic, masterFields, detailFields, init);
        }
        public UilNodeDetail AddDetail(string lid, string liid, string masterField, string detailField, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(lid, liid);
            return AddDetail(logic, masterField, detailField, init);
        }
        public UilNodeDetail AddDetail(string lid, string liid, Action<UilNodeDetail, LogicControl> init)
        {
            var logic = GetDetailLogic(lid, liid);
            return AddDetail(logic, init);
        }

        #endregion

        public string KeyField
        {
            get { return KeyFields != null && KeyFields.Length == 1 ? KeyFields[0] : null; }
            set { KeyFields = new[] { value }; }
        }

        public virtual void Assign(UilNode src)
        {
            Response = src.Response;
        }

        public Dictionary<UiKey, object> ResyncData;


        public UilResyncMode ResyncMode;
        public readonly UilResyncKeys ResyncKeys = new UilResyncKeys();
        /// <summary>
        /// Можно к ResyncKeys добавить keys, controls с которыми требуется обновить
        /// </summary>
        public virtual void GetResyncKeys() { }


        public virtual UilResyncKeys GetResyncKeys(UilResyncMode mode)
        {
            ResyncMode = mode;

            if ((mode & (UilResyncMode.Close | UilResyncMode.Delete)) == 0)
                ResyncKeys.Resync(Key, mode);
            //while (key != null)
            //{
            //    yield return key;
            //    key = key.MasterKey;
            //}
            if (DependentKeys != null)
            {
                foreach (var dkey in DependentKeys)
                {
                    if (dkey.MasterID == null)
                        dkey.MasterID = Key.MasterID;
                    var dlogic = Controller.Context.GetLogic(dkey);
                    if (KeyFields.Length == dlogic.KeyFields.Length)
                    {
                        for (var i = 0; i < KeyFields.Length; ++i)
                        {
                            var kf1 = KeyFields[i];
                            var kf2 = dlogic.KeyFields[i];
                            dkey[kf2] = Key.ID[kf1];
                        }
                    }
                    ResyncKeys.Resync(dkey, mode);
                }
            }
            GetResyncKeys();
            return ResyncKeys;
        }

        public Dictionary<UiKey, object> Resync(UilResyncMode mode)
        {
            if (ResyncData == null)
                ResyncData = new Dictionary<UiKey, object>();

            foreach (var key in GetResyncKeys(mode))
            {
                var logic = Controller.Context.GetLogic(key.Key);
                if (logic == null) continue;
                if (logic != this) logic.Assign(this);
                ResyncData[key.Key] = logic.GetResyncData(key);
                logic.ReloadDependentStores();
            }
            return ResyncData;
        }

        public void ReloadDependentStores()
        {
            if (DependentStoreIDs == null || DependentStoreIDs.Length == 0) return;
            var sb = new StringBuilder();
            var ctx = Controller.Context;
            foreach (var storeId in DependentStoreIDs)
            {
                if (ctx.ReloadedStores.Contains(storeId)) continue;
                ctx.ReloadedStores.Add(storeId);
                sb.AppendLine(storeId + ".reload();");
            }
            Response += sb.ToString();
        }

        /// <summary>
        /// Возвращает ControlLogic в указанном или текущем LogicNode (если lid is null). 
        /// Ищет по key, взятому из DetailKeys по lid и liid.
        /// Если не находит - создаёт новый.
        /// </summary>
        /// <param name="lid"></param>
        /// <param name="liid"></param>
        /// <returns></returns>
        protected UilNode GetDetailLogic(string lid, string liid)
        {
            var dnode = lid.IsEmpty() ? Controller : Controller.Context.GetController(lid);
            var dkey = Key.DetailKeys == null ? null
                : Key.DetailKeys.Where(a => a.LID == dnode.LID && a.LIID == liid).FirstOrDefault();
            return /*(ControlLogic)*/dnode.GetList(liid, dkey);// ?? dnode.GetForm(dkey);
        }
    }


    public class UilNodeDetail
    {
        public UilNode Logic;
        public string[] MasterFields;
        public string[] DetailFields;

        /// <summary>
        /// Вызывается перед подготовкой нового master-а
        /// </summary>
        public Action<UilNode, UilNodeDetail> MasterPreNew;
        /// <summary>
        /// Вызывается перед чтением существующего master-а
        /// </summary>
        public Action<UilNode, UilNodeDetail> MasterPreLoad;
        /// <summary>
        /// Вызывается перед сохранением master-а
        /// </summary>
        public Action<UilNode, UilNodeDetail> MasterPreSave;

        ///// <summary>
        ///// Для получения данных необходимы master-данные (Key.MasterID)
        ///// </summary>
        //public bool UseMaster;

        /// <summary>
        /// Если true - для MasterPreNew, MasterPreLoad необходима master-form-а (создаётся автоматически)
        /// </summary>
        public bool UseMasterForm;

        /// <summary>
        /// Имя поля в JSON, передаваемые форме на клиенте.
        /// Для случая, когда изменения detail-списока на форме должен запоминаться одновременно с формой.
        /// </summary>
        public string JsonListField { get; set; }

        public Action<UilNodeDetail, LogicControl> Init;
        public Control Control;

        public void OnInit(LogicControl container)
        {
            if (Logic.Key == null)
                Logic.Key = new UiKey(Logic);
            LoadMasterID(container.Logic.Key);

            if (Init != null) Init(this, container);
        }

        public virtual void LoadMasterID(UiKey mkey)
        {
            if (Logic.Key == null) return;
            Logic.Key.MasterKey = mkey;

            if (DetailFields == null) DetailFields = MasterFields;
            if (MasterFields == null) MasterFields = DetailFields;

            if (MasterFields == null)
            {
                if (mkey == null) return;
                Logic.Key.MasterID = mkey.ID;
            }
            else
            {
                if (mkey == null || mkey.ID == null || mkey.ID.Count < MasterFields.Length) return;
                var mid = Logic.Key.MasterID;
                if (mid != null)
                    mid.Clear();
                else
                    Logic.Key.MasterID = mid = new Dictionary<string, object>();

                for (var i = 0; i < MasterFields.Length; ++i)
                {
                    mid[DetailFields[i]] = mkey.ID[MasterFields[i]];
                }
            }
        }
    }

    public class UilNodeDetails : List<UilNodeDetail>
    {
        public UilNodeDetail Add<T>(T logic, string[] masterFields, string[] detailFields, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            var d = new UilNodeDetail
            {
                Logic = logic,
                MasterFields = masterFields,
                DetailFields = detailFields,
                Init = init
            };

            Add(d);
            return d;
        }
        public UilNodeDetail Add<T>(T logic, string masterField, string detailField, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            return Add(logic, new[] { masterField }, new[] { detailField }, init);
        }
        public UilNodeDetail Add<T>(T logic, Action<UilNodeDetail, LogicControl> init) where T : UilNode
        {
            var d = new UilNodeDetail { Logic = logic, Init = init };
            Add(d);
            return d;
        }

        public void Initialize(LogicControl container)
        {
            foreach (var item in this)
            {
                item.OnInit(container);
            }
        }
        public void LoadMasterID(UiKey mkey)
        {
            foreach (var item in this)
            {
                item.LoadMasterID(mkey);
            }
        }

        public void MasterPreNew(UilNode master)
        {
            foreach (var item in this)
            {
                if (item.MasterPreNew != null)
                    item.MasterPreNew(master, item);
            }
        }
        public void MasterPreLoad(UilNode master)
        {
            foreach (var item in this)
            {
                if (item.MasterPreLoad != null)
                    item.MasterPreLoad(master, item);
            }
        }
        public void MasterPreSave(UilNode master)
        {
            foreach (var item in this)
            {
                if (item.MasterPreSave != null)
                    item.MasterPreSave(master, item);
            }
        }

        public bool UseMasterForm()
        {
            foreach (var item in this)
            {
                if (item.UseMasterForm) return true;
            }
            return false;
        }

    }

}